package com.maikang.exammall.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.maikang.exammall.configuration.property.TxMapProperty;
import com.maikang.exammall.entity.Region;
import com.maikang.exammall.entity.constant.RedisKeyConstant;
import com.maikang.exammall.entity.dto.PositionDTO;
import com.maikang.exammall.service.RedisService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Log4j2
@RequiredArgsConstructor
@Component
public class MapUtil {

    private final TxMapProperty txMapProperty;
    private final RestTemplate restTemplate;
    private final RedisService redisService;
    private final RegionMapUtil regionMapUtil;


    /**
     * 默认地球半径
     */
    private static double EARTH_RADIUS = 6378137;//赤道半径(单位m)

    /**
     * 转化为弧度(rad)
     * */
    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }
    /**
     * @param lon1 第一点的经度
     * @param lat1 第一点的纬度
     * @param lon2 第二点的经度
     * @param lat2 第二点的纬度
     * @return 返回的距离，单位m
     * */
    public static Double getDistance(Double lon1, Double lat1, Double lon2, Double lat2) {
        if (lon1.equals(0.0) || lat1.equals(0.0) || lon2.equals(0.0) || lat2.equals(0.0)) {
            log.warn("longitude or latitude should not be zero");
            throw new RuntimeException("longitude or latitude should not be zero");
        }
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lon1) - rad(lon2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        return s;
    }

    public List<Double> parseLongitudeLatitudeFromAddressName(String address) throws URISyntaxException, UnsupportedEncodingException {
        List<Double> longLat = new ArrayList<>();

        String encodedAddress = URLEncoder.encode(address, "utf-8");
        String url = txMapProperty.getUrl() + "?address=" + encodedAddress + "&key=" + txMapProperty.getPositionKey();
        JsonNode node = restTemplate.getForObject(new URI(url), JsonNode.class);
        assert node != null;
        if (node.get("status").asInt() != 0) {
            throw new RuntimeException("response status is not 0,  response: " + node);
        }
        longLat.add(node.get("result").get("location").get("lng").asDouble());
        longLat.add(node.get("result").get("location").get("lat").asDouble());
        return longLat;
    }

    public PositionDTO parseLocationFromLongitudeAndLatitude(Double longitude, Double latitude) throws URISyntaxException {
        String url = txMapProperty.getUrl() + "?location=" + latitude + "," + longitude + "&key=" + txMapProperty.getPositionKey();
        JsonNode node = restTemplate.getForObject(new URI(url), JsonNode.class);
        assert node != null;
        if (node.get("status").asInt() != 0) {
            throw new RuntimeException("response status is not 0,  response: " + node);
        }

        JsonNode adInfo = node.get("result").get("ad_info");
        String nationCode = adInfo.get("nation_code").asText();
        String cityCode = adInfo.get("city_code").asText();
        Long cityId = Long.valueOf(cityCode.substring(nationCode.length()));
        Long countyId = adInfo.get("adcode").asLong();
        Long townId = node.get("result").get("address_reference").get("town").get("id").asLong();
        Map<Long, Region> map = regionMapUtil.getRegionIdToRegionMap();
        Long provinceId = map.get(cityId).getParentId();

        return PositionDTO.builder().longitude(longitude)
                .latitude(latitude)
                .provinceId(provinceId)
                .provinceName(map.get(provinceId).getName())
                .cityId(cityId)
                .cityName(map.get(cityId).getName())
                .countyId(countyId)
                .countyName(map.get(countyId).getName())
                .townId(townId)
                .townName(map.getOrDefault(townId, new Region()).getName())
                .build();
    }

    public void storePositionToRedis(Long userId, PositionDTO position) {
        redisService.set(RedisKeyConstant.getUserPositionKeyFromUserId(userId), position);
        log.info("store user position successfully, key: {}, position: {}", RedisKeyConstant.getUserPositionKeyFromUserId(userId), position);
    }
}
